//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

#ifndef COMPAK_H
#define COMPAK_H

#include <windows.h>
#include "../../psys/include/psys.h"
#include "pnetlib_com_basedef.h"
#include "pnetlib_packet_def.h"

//==================================================================
///
//==================================================================
class ComListener
{
	int		_listen_fd;
	int		_listen_port;

public:
	ComListener();
	~ComListener();

	int		StartListen( int port_number );
	void	StopListen();
	int		Idle( int &out_accepted_fd );

	int		GetListenPort() const
	{
		return _listen_port;
	}
};

//==================================================================
///
//==================================================================
struct CompakInpack
{
	u_int				_ticket_num;
	bool				_is_done;
	packet_header_t		_local_head;
	packet_header_t		*_head_and_datap;

	CompakInpack() :
		_ticket_num(0),
		_is_done(false),
		_head_and_datap(NULL)
	{
	}

	void Init( const packet_header_t &src_head )
	{
		if ( src_head.size == 0 )
		{
			_local_head = src_head;
			_head_and_datap = &_local_head;
		}
		else
		{
			size_t	alloc_size = sizeof(packet_header_t) + src_head.size;
			_head_and_datap = (packet_header_t *)(new char[ alloc_size ] );
			_head_and_datap[0] = src_head;
		}
	}

	void *GetData()
	{
		return (u_char *)(_head_and_datap + 1);
	}

	u_int GetDataSize() const
	{
		return _head_and_datap->size;
	}

	u_int GetFullSize() const
	{
		return sizeof(packet_header_t) + _head_and_datap->size;
	}

	bool IsAllocated() const
	{
		return _head_and_datap && (_head_and_datap->size != 0);
	}

	void Free()
	{
		if ( IsAllocated() )
			delete [] _head_and_datap;

		_head_and_datap = NULL;
	}
};

//==================================================================
struct CompakOutpack
{
	u_int				_ticket_num;
	packet_header_t		*_head_and_datap;
	u_int				_total_bytes;

	CompakOutpack() :
		_ticket_num(0),
		_head_and_datap(NULL),
		_total_bytes(0)
	{
	}

	void Free()
	{
		if ( _head_and_datap )
		{
			free( _head_and_datap );
			_head_and_datap = NULL;
		}
	}
};

//==================================================================
struct CompakStats
{
	u_long		_total_send_bytes;
	u_long		_total_recv_bytes;
	double		_start_timed;

	u_long		_send_bytes_queue;
	u_long		_recv_bytes_queue;

	u_long		_total_send_bytes_window;
	u_long		_total_recv_bytes_window;
	double		_start_timed_window;
	u_long		_window_size;

	float		_send_bytes_per_sec_window;
	float		_recv_bytes_per_sec_window;
	
	void Reset();
	void UpdateWindow( double timed );
};

//==================================================================
typedef void	(*CompackOnPackCb)( const void *datap, u_int data_size, void *userdatap );

//==================================================================
class Compak
{
	int					_status;
	bool				_connected_as_caller;
	int					_fd;
	double				_connection_started_time;

	u_int					_top_pack_done_bytes;
	std::vector<CompakOutpack>	_outpack_queue;

	packet_header_t			_cur_inpack_head;
	int						_cur_inpack_readbytes;
	std::vector<CompakInpack>	_inpack_queue;

	CompakStats			_stats;

	void				*_io_mutex_h;
	void				*_hservice;
	volatile int		_thread_status;

	bool				_need_disconnect;

	u_int				_on_pack_pkid;
	void				*_on_pack_userdatap;
	CompackOnPackCb		_on_pack_callback;

public:
	Compak();
	~Compak();

	int		Call( const TCHAR *ipnamep, int port_number );
	int		SetAcceptedConn( int fd );
	void	Disconnect();
	int		Idle();
	bool	IsConnectedAsCaller() const
	{
		return _connected_as_caller;
	}
	bool	IsConnected() const;

	bool	GetInputPack( u_int *out_pack_idp,
						  u_int *out_pack_data_sizep,
						  void *out_datap,
						  u_int out_data_max_size,
						  int n_pack_filter=0,
						  const u_int *filter_id_array=NULL );
	void	DisposeINPacket();

	void	*AllocPacket( u_short pk_id, int data_size );
	int		SendPacket( void *compak_allocated_datap, u_int *send_ticketp=0 );
	int		SendPacket( u_short pk_id, const void *datap=NULL, int data_size=0, u_int *send_ticketp=NULL );
	int		SearchOUTQueue( u_short packet_id );
	int		GetOUTQueueCnt() const;

	void				SetOnPackCallback( u_int pk_id, CompackOnPackCb callback, void *cb_userdatap )
						{
							_on_pack_pkid = pk_id;
							_on_pack_callback = callback;
							_on_pack_userdatap = cb_userdatap;
						}

	const CompakStats	&GetStats() const { return _stats;	}

private:
	int		handleSend();
	int		handleReceive();
	void	resetIOActivity();
	void	onConnect();
	int		onDisconnect( int printerror );
	void	disposeInPack( CompakInpack *ip );
	void	setStatus( int status );

	static DWORD WINAPI ioThread_s( Compak *mythis );
	DWORD				ioThread();
};

#endif
